type FailError = {
    code: number;
    msg: string;
};

type SearchReturnType<T> = T extends string[] ? string[] : Array<T>;

/**搜索选项泛型接口*/
export interface SearchOption<T> {
    data?: T extends string[] ? string[] : Array<T>;
    key?: T extends string[] ? never : keyof T;
    children?: string;
    value: string;
    isCompletely?: boolean;
    success?: (result: SearchReturnType<T>) => void;
    fail?: (error: FailError) => void;
}

/**
 * 搜索函数
 * @author zs.duan
 * @date 2024-09-03
 * @param {SearchOption<T>} option 配置对象
 * @param {Array} option.data 原始数组（可选，但建议传入）
 * @param {string} [option.key] 搜索的 key（对象数组时必填）
 * @param {string} [option.children] 子级字段名，默认 'children'
 * @param {string} option.value 搜索关键词
 * @param {boolean} [option.isCompletely] 是否完全匹配
 * @param {Function} [option.success] 成功回调
 * @param {Function} [option.fail] 失败回调
 * @returns {T extends string[] ? string[] : Array<T>} 返回匹配结果数组
 *
 * @example
 * Search({
 *   data: ['apple', 'banana'],
 *   value: 'ap',
 *   success: (res) => console.log(res)
 * })
 *
 * Search({
 *   data: [{ name: 'Alice' }, { name: 'Bob' }],
 *   key: 'name',
 *   value: 'Ali'
 * })
 */
export default function Search<T>(
    option: SearchOption<T>
): T extends string[] ? string[] : Array<T> {
    const defaultOptions: Required<Omit<SearchOption<T>, 'value' | 'data'>> = {
        key: undefined as any,
        children: 'children',
        isCompletely: false,
        success: () => { },
        fail: () => { },
    };

    const options = { ...defaultOptions, ...option };

    const { data, value, key, children, isCompletely, success, fail } = options;

    if (data == null || data === undefined) {
        const errorMsg = { code: -1, msg: '原始搜索数据不能为空' };
        fail?.(errorMsg);
        return [] as unknown as SearchReturnType<T>;
    }

    if (!Array.isArray(data)) {
        const errorMsg = { code: -1, msg: '原始搜索数据必须为数组' };
        fail?.(errorMsg);
        return [] as unknown as SearchReturnType<T>;
    }

    if (typeof value !== 'string') {
        const errorMsg = { code: -1, msg: '搜索关键词必须为字符串' };
        fail?.(errorMsg);
        return [] as unknown as SearchReturnType<T>;
    }
    if (data.length > 0 && typeof data[0] === 'object' && !key) {
        const errorMsg = { code: -1, msg: '数组对象时，搜索 key 不能为空' };
        fail?.(errorMsg);
        return [] as unknown as SearchReturnType<T>;
    }

    // 如果没有搜索关键词，返回原数组
    if (!value.trim()) {
        success?.(data);
        return data;
    }

    const result: any[] = [];

    // 搜索对象数组（通过 key）
    const searchByKey = (items: Array<Record<string, any>>) => {
        for (const item of items) {
            const fieldValue = item[key as string];
            const match = typeof fieldValue === 'string' || typeof fieldValue === 'number'
                ? isCompletely
                    ? String(fieldValue) === value
                    : String(fieldValue).toLowerCase().includes(value.toLowerCase())
                : false;

            if (match) {
                result.push({ ...item }); 
            }
            if (children && item[children] && Array.isArray(item[children])) {
                searchByKey(item[children]);
            }
        }
    };

    // 搜索字符串数组
    const searchByValue = (items: any[]) => {
        for (const item of items) {
            const match = isCompletely
                ? String(item) === value
                : String(item).toLowerCase().includes(value.toLowerCase());

            if (match) {
                result.push(item);
            }
            if (children && item[children] && Array.isArray(item[children])) {
                searchByValue(item[children]);
            }
        }
    };

    // 执行搜索
    if (key) {
        searchByKey(data as Array<Record<string, any>>);
    } else {
        searchByValue(data);
    }
    success?.(result as SearchReturnType<T>);
    return result as SearchReturnType<T>;
}